/* 测试系统是否支持强制性锁机制 */
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    struct stat sbuf = {0};
    pid_t pid;
    int fd = -1;

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        exit(-1);
    }
    /* 打开文件 */
    fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0664);
    if (-1 == fd) {
        perror("open error");
        exit(-1);
    }
    /* 写入一行字符串 */
    if (12 != write(fd, "Hello World", 12)) {
        perror("write error");
        exit(-1);
    }
    /* 开启强制性锁机制 */
    if (0 > fstat(fd, &sbuf)) { /* 获取文件属性 */
        perror("fstat error");
        exit(-1);
    }
    if (0 > fchmod(fd, (sbuf.st_mode & ~S_IXGRP | S_ISGID))) { 
        perror("fchmod error");
        exit(-1);
    }
    /* 创建子进程 */
    if (0 > (pid = fork())) { /* 出错 */
        perror("fork error");
    } else if (0 < pid) { /* 父进程 */
        struct flock lock = { 0 };
        /* 对整个文件加写锁 */
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        if (0 > fcntl(fd, F_SETLKW, &lock)) { /* 出错 */
            perror("父进程：加写锁失败");
        } else {
            printf("父进程:加写锁成功\n");
        }
        printf("~~~~~~~~~~~~~~~~");
        if (0 > wait(NULL)) {
            perror("wait error");
        }
    } else { /* 子线程 */
        struct flock lock = { 0 };
        int flag;
        char buf[20] = { 0 };
        sleep(1); /* 休眠1s，让父进程先运行 */
        /* 设置为非阻塞方式 */
        flag = fcntl(fd, F_GETFL);
        flag |= O_NONBLOCK;
        fcntl(fd, F_SETFL, flag);
        /* 对整个文件加读锁 */
        lock.l_type = F_RDLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        if (0 > fcntl(fd, F_SETLK, &lock)) { /* 出错 */
            perror("子进程:加读锁失败");
        } else {
            printf("子进程:加读锁成功\n");
        }
        /* 读文件 */
        if ( 0 > lseek(fd, 0, SEEK_SET)) { 
            perror("lseek error");
        }
        if (0 > read(fd, buf, 12)) {
            perror("子进程：read error");
        } else {
            printf("子进程：read OK, buf= %s\n", buf);
        }
    }
    exit(0);
}